///////////////////////////////////////////////////////////////////////////
//  Brainfreeze - Yet another sokoban puzzle game, this time in 3D
//  Copyright (C) <year>  <name of author>
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License along
//  with this program; if not, write to the Free Software Foundation, Inc.,
//  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
///////////////////////////////////////////////////////////////////////////
// bf.cpp - this is the main file, where everything is bootstrapped

#include "stdafx.h"
#include "bf.h"
#include "ExampleApplication.h"

GameSession	*session;
InputController *controller;
GameRenderer3D *renderer;

// Declare a subclass of the ExampleFrameListener class
class MyListener : public ExampleFrameListener
{
	public:
		MyListener(RenderWindow* win, Camera* cam, SceneManager *sceneMgr) : ExampleFrameListener(win, cam)
		{
			mSceneMgr = sceneMgr;
		}

		bool processUnbufferedKeyInput(const FrameEvent& evt)
		{
			//
			// check the amount of time since the last key press. If its
			// less than half a second, don't process. This will help to
			// prevent an overly sensitive input manager from firing too
			// many keys :)
			//
			static Ogre::Real elapasedTime = 0;
			bool canProcessThisEvent = false;

			if( elapasedTime < 0.1 )
			{
				elapasedTime += evt.timeSinceLastFrame;
			}
			else
			{
				canProcessThisEvent = true;
			}canProcessThisEvent = true;

			//
			// grab any moves the player might try to make
			//
			bool handled = false;

			if( mInputDevice->isKeyDown( Ogre::KC_DOWN ) && canProcessThisEvent )
			{
				handled = true;
				controller->MovePlayerDown(*renderer, *session);
			}
			else if ( mInputDevice->isKeyDown( Ogre::KC_UP ) && canProcessThisEvent )
			{
				handled = true;
				controller->MovePlayerUp(*renderer, *session);
			}
			else if ( mInputDevice->isKeyDown( Ogre::KC_LEFT ) && canProcessThisEvent )
			{
				handled = true;
				controller->MovePlayerLeft(*renderer, *session);
			}
			else if ( mInputDevice->isKeyDown( Ogre::KC_RIGHT ) && canProcessThisEvent )
			{
				handled = true;
				controller->MovePlayerRight(*renderer, *session);
			}
			else if ( mInputDevice->isKeyDown( Ogre::KC_R ) && canProcessThisEvent )
			{
				handled = true;
				controller->ResetLevel(*renderer, *session);
			}

			//
			// Update all of the location of the boxes
			//
			if( handled )
			{
				elapasedTime = 0;
				renderer->UpdateWorld(*session);
			}

			//
			// Check to see if we handled the keyboard event. If not, give our base frame listener
			// a crack at it
			//
			return (handled ? true : ExampleFrameListener::processUnbufferedKeyInput(evt));
		}

		///////////////////////////////////////////////////////////////////
		// EVENT: Start of frame rendering
		///////////////////////////////////////////////////////////////////
		bool frameStarted(const FrameEvent& evt)
		{
			return ExampleFrameListener::frameStarted(evt);    
		}

		///////////////////////////////////////////////////////////////////
		// EVENT: Finished frame rendering
		///////////////////////////////////////////////////////////////////
		bool frameEnded(const FrameEvent& evt)
		{
			return ExampleFrameListener::frameEnded(evt);        
		}

	protected:
		SceneManager* mSceneMgr;
};

////////////////////////////////////////////////////////////////////////////
// Declare a subclass of the ExampleApplication class
////////////////////////////////////////////////////////////////////////////
class SampleApp : public ExampleApplication 
{
public:
   SampleApp() 
   {
	   session = new GameSession();
   }

protected:

	//////////////////////////////////////////////////////////////////////////
	// Build the scene before we start displaying it
	//////////////////////////////////////////////////////////////////////////
	void createScene(void)
	{
		//
		// allocate the game renderer. There's probably a prettier location to do
		// it in, but right now I want it to work :)
		//
		renderer = new GameRenderer3D( mSceneMgr, mCamera );


		// create the ground
		renderer->CreateGround(*session);

		// now create the player
		renderer->CreatePlayer(*session);

		int width	= session->GetLevel()->Width();
		int height	= session->GetLevel()->Height();

		for( int x = 0; x < width; x++ )
		{
			for( int y = 0; y < height; y++ )
			{
				//
				// we'll need to render something different for each tile type
				//
				switch( session->GetLevel()->GetTileType( Vector3(x, y, 0) ) )
				{
					case ENTITY_FLOOR:
						// add a floor
						renderer->CreateFloorPiece( *session, x, y );
						break;

					case ENTITY_WALL:
						// add a wall block
						renderer->CreateWallPiece( *session, x, y );
						break;

					default:
						break;
				}
			}
		}

		//
		// Grab a list of all the goals for the current level, and then create a mesh to 
		// represent each on the grid
		//
		for( int i = 0; i < session->GetLevel()->GetGoalCount(); i++ )
		{
			renderer->CreateCrate( *session, session->GetLevel()->GetBox(i), i );
		}

		//
		// now, grab a list of all the boxes for the current level, then create them on
		// the playing board
		//
		for( int i = 0; i < session->GetLevel()->GetBoxCount(); i++ )
		{
			renderer->CreateGoal( *session, session->GetLevel()->GetGoal(i), i );
		}

		//
		// create da camera
		//
		mCamera->setPosition( Vector3( 400, 200, 400 ) );
		mCamera->lookAt( Vector3( 0, 0, 0 ) );

   }
  
   ////////////////////////////////////////////////////////////////////////
   // Create new frame listener
   ////////////////////////////////////////////////////////////////////////
   void createFrameListener(void)
   {
       mFrameListener = new MyListener(mWindow, mCamera, mSceneMgr);
       mRoot->addFrameListener(mFrameListener);
   }
};

#ifdef __cplusplus
extern "C" {
#endif

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 
#define WIN32_LEAN_AND_MEAN 
#include "windows.h" 
INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT) 
#else 
int main(int argc, char **argv) 
#endif 
{
    // Instantiate our subclass
    SampleApp myApp;
	controller = new InputController();

    try 
	{
        // ExampleApplication provides a go method, which starts the rendering.
        myApp.go();
    }
    catch (Ogre::Exception& e) {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 
        MessageBoxA(NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
        std::cerr << "Exception:\n";
        std::cerr << e.getFullDescription().c_str() << "\n";
#endif
        return 1;
    }
	catch (std::exception& e)
	{
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 
		MessageBoxA(NULL, e.what(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
        std::cerr << "Exception:\n";
        std::cerr << e.e.what() << "\n";
#endif
        return 1;
	}

return 0;
}

#ifdef __cplusplus
}
#endif